home *** CD-ROM | disk | FTP | other *** search
/ SGI Developer Toolbox 6.1 / SGI Developer Toolbox 6.1 - Disc 4.iso / src / apps / pcnfsd / server / msgserv.c < prev    next >
Encoding:
C/C++ Source or Header  |  1994-08-02  |  13.5 KB  |  733 lines

  1. /* RE_SID: @(%)/tmp_mnt/vol/dosnfs/shades_SCCS/unix/rfcmsg/server/src/SCCS/s.msgserv.c 1.6 93/02/08 10:25:17 SMI */
  2. /* @(#)msgserv.c    1.6 2/8/93 */
  3. /*
  4.  * msgserv.c
  5.  *
  6.  * Copyright (c) 1991-1993 Sun Microsystems, Inc. 
  7.  *
  8.  * This is a server implementation of the Message Send protocol
  9.  * defined in RFCxxxx. This implementation may be freely
  10.  * copied, modified, and redistributed, provided that this
  11.  * comment and the Sun Microsystems copyright are retained.
  12.  * Anyone installing, modifying, or documenting this
  13.  * software is advised to read the section in the RFC which
  14.  * deals with security issues.
  15.  */
  16. #include <sys/types.h>
  17. #include <sys/time.h>
  18. #include <sys/stat.h>
  19. #include <utmp.h>
  20. #ifdef SVR4
  21. #include <sys/select.h>
  22. #include <sys/resource.h>
  23. #endif
  24. #include <sys/socket.h>
  25. #ifndef SVR4
  26. #include <sys/ttycom.h>
  27. #endif
  28. #include <sys/wait.h>
  29. #include <sys/sockio.h>
  30. #include <netinet/in.h>
  31. #include <net/if.h>
  32. #include <arpa/inet.h>
  33. #include <netdb.h>
  34. #include <stdio.h>
  35. #include <fcntl.h>
  36. #include <string.h>
  37. #include <signal.h>
  38. #include <ctype.h>
  39. #include <time.h>
  40. #include <memory.h>
  41.  
  42. char *prog;
  43. int debug = 0;
  44. int verbose = 0;
  45. int use_console = 0;    /* XXX currently unused */
  46. char *empty_arg = "";
  47.  
  48. char * recipient;
  49. char * recip_term;
  50. char * sender;
  51. char * sender_term;
  52. char * msg_text;
  53. char * cookie;
  54. char * signature;
  55.  
  56. char console[] = "/dev/console";
  57. /* utmp globals */
  58. struct utmp utmp;
  59. FILE *utmp_file;    /* stream for utmp, also non-0 indicates valid entry */
  60.  
  61.  
  62. /*
  63.  * types for all procedures
  64.  */
  65. void usage();
  66. void handle_udp();
  67. void handle_tcp();
  68. int main();
  69. #ifdef SVR4
  70. void reaper();
  71. #else
  72. int reaper();
  73. #endif
  74. void udp_ack();
  75. void ack();
  76. void nak();
  77. char *deliver();
  78. int rip_apart_message();
  79. int check_cache();
  80. void filter();
  81. char *do_cmd();
  82. char * first_utmp_entry();
  83. void next_utmp_entry();
  84. char *send_to_term();
  85.  
  86.  
  87. void
  88. usage()
  89. {
  90.     fprintf(stderr, "usage: %s [-d][-pN]\n", prog);
  91. /* XXX    fprintf(stderr, "usage: %s [-d][-pN][-c]\n", prog); */
  92.     fprintf(stderr, "  -d    - turn on debugging\n");
  93.     fprintf(stderr, "  -pN   - use port N (default: 18)\n");
  94. /* XXX    fprintf(stderr, "  -c    - ignore terminal and use console\n"); */
  95.  
  96. }
  97.  
  98. int
  99. main(argc, argv)
  100. int argc;
  101. char *argv[];
  102. {
  103.  
  104.     short port = 0;
  105.     int tcpsock1;
  106.     int tcpsock2;
  107.     int udpsock;
  108.     int i;
  109.     fd_set ready;
  110.     struct sockaddr_in sin;
  111.     struct servent *sp;
  112.  
  113. #ifdef SVR4
  114.     int nfds;
  115.     struct rlimit rlp;
  116.  
  117.     if (getrlimit(RLIMIT_NOFILE, &rlp) && debug)
  118.         perror("getrlimit");
  119.     nfds = rlp.rlim_cur;
  120. #else
  121.     int nfds = getdtablesize();
  122. #endif
  123.  
  124.  
  125.     prog = *argv++;
  126.     argc--;
  127.  
  128.     /* process options:
  129.      * -d (debug)
  130.      * -pN (use port N instead of 18)
  131.      */
  132.  
  133.     while(argc && *argv[0] == '-') {
  134.         (*argv)++;
  135.         switch (toupper(*argv[0])){
  136.             case 'D':
  137.                 debug++;
  138.                 verbose++;
  139.                 break;
  140.             case 'C':
  141.                 use_console++;
  142.                 break;
  143.             case 'P':
  144.                 (*argv)++;
  145.                 port = atoi(*argv);
  146.                 break;
  147.             default:
  148.                 usage();
  149.                 exit(1);
  150.                 /*NOTREACHED*/
  151.         }
  152.         argv++;
  153.         argc--;
  154.     }
  155.     if(argc != 0) {
  156.         usage();
  157.         exit(1);
  158.         /*NOTREACHED*/
  159.     }
  160.  
  161.     if(!debug) {
  162.         if(fork())
  163.             exit(0);
  164.         for(i = nfds-1; i >= 0; --i)
  165.             close(i);
  166.         (void)open("/dev/null", O_RDWR);
  167.         dup2(0, 1);
  168.         dup2(0, 2);
  169. #ifdef SVR4
  170.         setsid();
  171. #else
  172. /* NB - setsid() also works in SunOS but maybe not other BSD-derived code */
  173.         i = open("/dev/tty", O_RDWR);
  174.         if(i >= 0){
  175.             ioctl(i, TIOCNOTTY, 0);
  176.             close(i);
  177.         }
  178. #endif
  179. /* XXX todo - add code to use SYSLOG if we're not in debug mode */
  180.     }
  181.  
  182.     signal(SIGCHLD, reaper);
  183.  
  184.     sin.sin_family = AF_INET;
  185.  
  186.     /*
  187.      * compute the port to use: consult /etc/services, but if not
  188.      * found use 18 (from the RFC). the -pN option overrides
  189.      */
  190.  
  191.     if(port == 0) {
  192.         sp = getservbyname("message", "udp");
  193.         if(sp)
  194.             sin.sin_port = sp->s_port;
  195.         else
  196.             sin.sin_port = htons(18);    /* from the RFC */
  197.     }
  198.     else
  199.         sin.sin_port = htons(port);
  200.  
  201.     sin.sin_addr.s_addr = INADDR_ANY;
  202.  
  203.     if(debug) printf("%s: using port %d\n", prog, htons(sin.sin_port));
  204.  
  205.  
  206.  
  207.     tcpsock1 = socket(AF_INET, SOCK_STREAM, 0);
  208.     if(bind(tcpsock1, (struct sockaddr *)&sin, sizeof sin) < 0) {
  209.         if(debug) perror("bind (TCP)");
  210.         exit(99); /* XXX */
  211.     }
  212.     listen(tcpsock1, 5);
  213.  
  214.     udpsock = socket(AF_INET, SOCK_DGRAM, 0);
  215.     if(bind(udpsock, (struct sockaddr *)&sin, sizeof sin) < 0) {
  216.         if(debug) perror("bind (UDP)");
  217.         exit(99); /* XXX */
  218.     }
  219.  
  220.     if(debug) printf("entering main loop...\n");
  221.     while(1) {
  222.         FD_ZERO(&ready);
  223.         FD_SET(udpsock, &ready);
  224.         FD_SET(tcpsock1, &ready);
  225.         i = select(nfds, &ready, (fd_set *)0, (fd_set *)0, (struct timeval *)0);
  226.         if(debug) printf("----------------------------------------------------------\nselect returned %d\n", i);
  227.         if(i <= 0) {
  228.             if(debug)perror("select");
  229.             continue;
  230.         }
  231.         if(FD_ISSET(udpsock, &ready))
  232.             handle_udp(udpsock);
  233.         else if(FD_ISSET(tcpsock1, &ready)) {
  234.             tcpsock2 = accept(tcpsock1, (struct sockaddr *)0,
  235.                 (int *)0);
  236.  
  237.             if(debug)
  238.                 printf("forking....\n");
  239.  
  240.             if(fork() == 0) {
  241.                 close(tcpsock1);
  242.                 close(udpsock);
  243.                 handle_tcp(tcpsock2);
  244.                 exit(0);
  245.             }
  246.         }
  247.     }
  248. }
  249.  
  250. #define CACHE_ENTRIES 32
  251.  
  252. struct mc_entry {
  253.     struct sockaddr_in mc_addr;
  254.     char mc_cookie[48];
  255. };
  256.  
  257. int mc_used = 0;
  258. int mc_next = 0;
  259. struct mc_entry mcache[CACHE_ENTRIES];
  260.  
  261. int
  262. check_cache(cookie, addrp)
  263. char *cookie;
  264. struct sockaddr_in *addrp;
  265. {
  266.     int i;
  267.     if(mc_used) {
  268.         for (i = 0; i < mc_used; i++) {
  269.             if(!strcmp(cookie, mcache[i].mc_cookie) &&
  270.                !memcmp((char *)addrp, (char *)&mcache[i].mc_addr,
  271.                 sizeof(*addrp)))
  272.                 return(1);
  273.         }
  274.     }
  275.     memcpy((char *)&mcache[mc_next].mc_addr, (char *)addrp, sizeof(*addrp));
  276.     strcpy(mcache[mc_next].mc_cookie, cookie);
  277.  
  278.     mc_next++;
  279.     if(mc_next > mc_used) mc_used = mc_next;
  280.     mc_next = mc_next % CACHE_ENTRIES;
  281.     return(0);
  282. }
  283.  
  284. void
  285. handle_udp(s)
  286. int s;
  287. {
  288.     char buff[1024];
  289.     int buflen;
  290.     struct sockaddr_in from;
  291.     int fromlen;
  292.     char *txt;
  293.  
  294.     fromlen = sizeof (from);
  295.  
  296.     if(debug) printf("%s: udp msg received\n", prog);
  297.  
  298.     buflen = recvfrom(s, buff, 1024, 0,
  299.          (struct sockaddr *)&from,  &fromlen);
  300.  
  301.     if(buflen < 0) {
  302.         perror("recvfrom");
  303.         return;
  304.     }
  305.     if(rip_apart_message(buff, buflen)) {
  306.         fprintf(stderr, "%s: malformed message\n", prog);
  307.         return;
  308.     }
  309.     if(check_cache(cookie, &from)) {
  310.         if(debug) printf("duplicate message\n");
  311.         return;
  312.     }
  313.  
  314.     if(debug)
  315.         printf("forking....\n");
  316.  
  317.     if(!debug) {
  318.         if(fork() != 0)
  319.             return;
  320.     }
  321.  
  322.     if((txt = deliver()) == NULL && *recipient) {
  323.         udp_ack(s, &from, "OK");
  324.     }
  325.     if(!debug)
  326.         exit(0);
  327. }
  328.  
  329. void
  330. handle_tcp(s)
  331. int s;
  332. {
  333.     char buff[1024];
  334.     int buflen;
  335.     struct sockaddr_in peer;
  336.     int peerlen;
  337.     char *txt;
  338.  
  339.     if(debug) printf("%s: tcp msg received\n", prog);
  340.  
  341.     peerlen = sizeof peer;
  342.     if(getpeername(s, &peer, &peerlen) < 0) {
  343.         perror("getpeername");
  344.         exit(99);
  345.     }
  346.  
  347.     buflen = read(s, buff, 1024);
  348.     if(buflen < 0) {
  349.         perror("read");
  350.         nak(s, "Read error");
  351.         return;
  352.     }
  353.     if(rip_apart_message(buff, buflen)) {
  354.         fprintf(stderr, "%s: malformed message\n", prog);
  355.         nak(s, "Message format error");
  356.         return;
  357.     }
  358.     if((txt = deliver()) != NULL) {
  359. #ifdef notyet
  360.         nak(s, txt);
  361.         return;
  362. #endif
  363.     }
  364.     ack(s, "OK");
  365. }
  366.  
  367.  
  368. /* Note the type difference here */
  369.  
  370. #ifdef SVR4
  371. void
  372. reaper()
  373. {
  374.     int i, j;
  375.     i = wait(&j);
  376.     return;
  377. }
  378. #else
  379. int
  380. reaper()
  381. {
  382.     union wait status;
  383.     while(wait3(&status, WNOHANG, 0) > 0) continue;
  384.     return(0);
  385. }
  386. #endif
  387.  
  388. void udp_ack(s, to, msg)
  389. int s;
  390. struct sockaddr_in *to;
  391. char *msg;
  392. {
  393.     char buff[128];
  394.     if(debug)
  395.         printf("sending ack\n");
  396.     sprintf(buff, "+%s", msg);
  397.     (void)sendto(s, buff, strlen(buff) + 1, 0,
  398.         (struct sockaddr *)to, sizeof (*to));
  399. }
  400.  
  401. void ack(s, msg)
  402. int s;
  403. char *msg;
  404. {
  405.     char buff[128];
  406.     if(debug)
  407.         printf("sending ack\n");
  408.     sprintf(buff, "+%s", msg);
  409.     (void)write(s, buff, strlen(buff) + 1);
  410. }
  411.  
  412. void nak(s, msg)
  413. int s;
  414. char *msg;
  415. {
  416.     char buff[128];
  417.     if(debug)
  418.         printf("sending nak\n");
  419.     sprintf(buff, "-%s", msg);
  420.     (void)write(s, buff, strlen(buff) + 1);
  421. }
  422.  
  423. int
  424. rip_apart_message(buff, buflen)
  425. char *buff;
  426. int buflen;
  427. {
  428.     char *cp1;
  429.     char *cp2;
  430.     char *lim;
  431.  
  432.  
  433.     recipient = NULL;
  434.     recip_term = NULL;
  435.     sender = NULL;
  436.     sender_term = NULL;
  437.     msg_text = NULL;
  438.     cookie = NULL;
  439.     signature = NULL;
  440.  
  441.     if(buff[0] != 'B')
  442.         return(1);
  443.  
  444.     lim = &buff[buflen];
  445.  
  446.     cp1 = buff;
  447.     cp1++;                /* point at recipient */
  448.  
  449. /*
  450.  * Gather up recipient
  451.  */
  452.     cp2 = cp1;
  453.     while (*cp2 && cp2 < lim)
  454.         cp2++;
  455.     if(cp2 >= lim) return(1);    /* over-length */
  456.     recipient = cp1;
  457.     cp1 = cp2;
  458.     cp1++;
  459.     if(debug)
  460.         printf("recipient   = '%s'\n", recipient);
  461.  
  462.  
  463.  
  464. /*
  465.  * Gather up recip_term
  466.  */
  467.     cp2 = cp1;
  468.     while (*cp2 && cp2 < lim)
  469.         cp2++;
  470.     if(cp2 >= lim) return(1);    /* over-length */
  471.     recip_term = cp1;
  472.  
  473.     /* toss preceding "/dev/" if any */
  474.     if (strncmp(recip_term, "/dev/", strlen("/dev/")) == 0)
  475.         recip_term += strlen("/dev/");
  476.     cp1 = cp2;
  477.     cp1++;
  478.     if(debug)
  479.         printf("recip_term  = '%s'\n", recip_term);
  480.  
  481.  
  482. /*
  483.  * Gather up msg_text
  484.  */
  485.     cp2 = cp1;
  486.     while (*cp2 && cp2 < lim)
  487.         cp2++;
  488.     if(cp2 >= lim) return(1);    /* over-length */
  489.     msg_text = cp1;
  490.     cp1 = cp2;
  491.     cp1++;
  492.     if(debug)
  493.         printf("msg_text    = '%s'\n", msg_text);
  494.  
  495.  
  496. /*
  497.  * Gather up sender
  498.  */
  499.     cp2 = cp1;
  500.     while (*cp2 && cp2 < lim)
  501.         cp2++;
  502.     if(cp2 >= lim) return(1);    /* over-length */
  503.     sender = cp1;
  504.     cp1 = cp2;
  505.     cp1++;
  506.     if(debug)
  507.         printf("sender      = '%s'\n", sender);
  508.  
  509.  
  510. /*
  511.  * Gather up sender_term
  512.  */
  513.     cp2 = cp1;
  514.     while (*cp2 && cp2 < lim)
  515.         cp2++;
  516.     if(cp2 >= lim) return(1);    /* over-length */
  517.     sender_term = cp1;
  518.     cp1 = cp2;
  519.     cp1++;
  520.     if(debug)
  521.         printf("sender_term = '%s'\n", sender_term);
  522.  
  523.  
  524. /*
  525.  * Gather up cookie
  526.  */
  527.     cp2 = cp1;
  528.     while (*cp2 && cp2 < lim)
  529.         cp2++;
  530.     if(cp2 >= lim) return(1);    /* over-length */
  531.     cookie = cp1;
  532.     cp1 = cp2;
  533.     cp1++;
  534.     if(debug)
  535.         printf("cookie      = '%s'\n", cookie);
  536.  
  537. /*
  538.  * Gather up signature
  539.  */
  540.     cp2 = cp1;
  541.     while (*cp2 && cp2 < lim)
  542.         cp2++;
  543.     if(cp2 >= lim) return(1);    /* over-length */
  544.     signature = cp1;
  545.     cp1 = cp2;
  546.     cp1++;
  547.     if(debug)
  548.         printf("signature   = '%s'\n", signature);
  549.  
  550.     return(0);
  551. }
  552.  
  553.  
  554. /*
  555.  * delivers the message; returns NULL if OK, otherwise
  556.  * a string describing the problem
  557.  */    
  558. char *deliver()
  559. {
  560.     char *cp;
  561.     int only_one;
  562.     char *retval;
  563.  
  564.     while(cp = strchr(msg_text, '\015'))
  565.         *cp = ' ';
  566.  
  567.     filter(msg_text);
  568.     if(debug) printf("delivering message....\n");
  569.  
  570.     /* set only_one to false only if recip_term is "*" */
  571.     only_one = 1;
  572.     if (strcmp(recip_term, "*") == 0) {
  573.         only_one = 0;
  574.     }
  575.  
  576.     /* go through utmp entries, sending to appropriate ones */
  577.     if (retval = first_utmp_entry())
  578.         return(retval);
  579.  
  580.     do {
  581.         if (debug) printf("evaluating utmp entry %s %s...\n",
  582.             utmp.ut_name, utmp.ut_line);
  583.         /* check for wrong recipient */
  584.         if (*recipient) {
  585.             if (strncmp(recipient, utmp.ut_name,
  586.                     sizeof(utmp.ut_name))) {
  587.                 continue;
  588.             }
  589.         }
  590.         else {
  591.             /* no recipient; if no term force console */
  592.             if (*recip_term == '\0') {
  593.                 if (strncmp("console", utmp.ut_line,
  594.                         sizeof(utmp.ut_line))) {
  595.                     /* nope, wrong term */
  596.                     continue;
  597.                 }
  598.             }
  599.         }
  600.  
  601.         /* check for wrong term */
  602.         if (*recip_term) {
  603.             /* specific term or "*" */
  604.             if (strcmp(recip_term, "*")) {
  605.                 /* specific term */
  606.                 if (strncmp(recip_term, utmp.ut_line,
  607.                         sizeof(utmp.ut_line))) {
  608.                     /* nope, wrong term */
  609.                     continue;
  610.                 }
  611.             }
  612.         }
  613.  
  614.         /* passed all tests, send it */
  615.         retval = send_to_term(utmp.ut_line);
  616.  
  617.         /* see if once is enough */
  618.         if (only_one && (retval == NULL))
  619.             break;
  620.  
  621.     } while (next_utmp_entry(), utmp_file);    /* keep going if more entries */
  622.     
  623.     return(NULL);
  624. }
  625.  
  626. /*
  627.  * first_utmp_entry
  628.  *
  629.  * opens utmp, calls next_utmp_entry and returns NULL if open is successful;
  630.  * returns error string if open fails.
  631.  */
  632. char *
  633. first_utmp_entry()
  634. {
  635.     memset((char *) &utmp, 0, sizeof(utmp));
  636.     utmp_file = fopen("/etc/utmp", "r");
  637.     if(utmp_file == NULL) {
  638.         perror("fopen /etc/utmp");
  639.         return("unable to open /etc/utmp\n");
  640.     }
  641.     next_utmp_entry();
  642.     return NULL;
  643. }
  644.  
  645. /*
  646.  * next_utmp_entry
  647.  *
  648.  * sets up next utmp entry with utmp_ok != 0,
  649.  * closes file and utmp_ok == 0 if none.
  650.  */
  651. void
  652. next_utmp_entry()
  653. {
  654.     while (fread((char *) &utmp, sizeof(utmp), 1, utmp_file) == 1) {
  655.         if (*utmp.ut_line && *utmp.ut_name) {
  656.             return;        /* utmp_file is non-NULL */
  657.         }
  658.     }
  659.     /* if we get here, we're at eof; close & zero utmp_file */
  660.     (void) fclose(utmp_file);
  661.     utmp_file = NULL;    /* indicates no more entries */
  662. }
  663.  
  664. char *
  665. send_to_term(term)
  666.     char *term;
  667. {
  668.     /*
  669.      * write to specific terminal if any, else console
  670.      */
  671.     char device[32];
  672.     struct stat statbuf;
  673.     int rc;
  674.     FILE *stream;
  675.     time_t aclock;
  676.  
  677.     sprintf(device, "/dev/%s", term);
  678.     if (stat(device, &statbuf)) {
  679.         perror("stat");
  680.         return("unable to stat %s", device);
  681.     }
  682.     
  683.     if (! (statbuf.st_mode & (S_IWGRP | S_IWOTH))) {
  684.         if (debug) printf("won't write to %s because mode is %o\n",
  685.             device, statbuf.st_mode);
  686.         return("won't write because of mode");
  687.     }
  688.  
  689.     stream = fopen(device, "w");
  690.     if(stream == NULL) {
  691.         perror("fopen");
  692.         return("unable to write to %s", device);
  693.     }
  694.     time(&aclock);
  695.     if(debug) printf("writing to %s\n", device);
  696.     fprintf(stream,
  697.         "\7\n---------\nConsole message from %s on %s%s---------\n",
  698.         sender, asctime(localtime(&aclock)), msg_text);
  699.     rc = fclose(stream);
  700.     if (rc) {
  701.         perror("fclose");
  702.         return("unable to close %s", device);
  703.     }
  704.     if(debug) printf("delivery successful\n");
  705.     return NULL;
  706. }
  707.  
  708. /*
  709.  * As noted in the RFC, it is important to filter out control
  710.  * chracters and suchlike, since there may exist terminals
  711.  * which will behave in bizarre and security-violating ways
  712.  * if presented with certain control code sequences. The
  713.  * client may also be filtering messages, but it is incumbent
  714.  * upon a well-written server implementation not to rely on being
  715.  * sent only "clean" messages.
  716.  *
  717.  * It is an open question as to how the filtering should be done.
  718.  * One approach might be to squeeze out any invalid characters
  719.  * silently. This would make debugging difficult. This implementation
  720.  * replaces all non-printable characters with '?' characters.
  721.  */
  722.  
  723. void
  724. filter(text)
  725. char *text;
  726. {
  727.     while (*text) {
  728.         if(!isprint(*text) && !isspace(*text))
  729.             *text = '?';
  730.         text++;
  731.     }
  732. }
  733.